import React, { memo, useCallback, useEffect, useRef, useState } from 'react';
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
import { getLyricRequest, getSongUrl } from '@/api/request';
import MiniPlayer from './miniPlayer';
import NormalPlayer from './normalPlayer';
import {
  changePlayingStateAction,
  changeShowPlayListAction,
  changeCurrentIndexAction,
  changeCurrentSongAction,
  changePlayModeAction,
  changeFullScreenAction,
  changePlayListAction,
} from './store/actionCreators';
import { isEmptyObject, findIndex, shuffle } from '@/api/utils';
import { playMode } from '@/api/config';
import Toast from '@/baseUI/Toast';
import PlayList from './play-list';
import Lyric from '@/api/lyric-parser';

export default memo(function Player() {
  // 目前播放时间
  const [currentTime, setCurrentTime] = useState(0);
  // 歌曲总时长
  const [duration, setDuration] = useState(0);
  //歌曲播放进度
  const percent = isNaN(currentTime / duration) ? 0 : currentTime / duration;
  //绑定ref
  const audioRef = useRef();
  //记录当前的歌曲，以便于下次重渲染时比对是否是一首歌
  const [preSong, setPreSong] = useState({});

  const [modeText, setModeText] = useState('');
  const toastRef = useRef();
  const songReady = useRef(true);
  // 歌词
  const currentLyric = useRef();
  const {
    fullScreen,
    playing,
    currentIndex,
    currentSong,
    mode, //播放模式
    sequencePlayList, //顺序列表
    playList,
    showPlayList,
  } = useSelector(
    (state) => ({
      fullScreen: state.player.fullScreen,
      playing: state.player.playing,
      currentSong: state.player.currentSong,
      showPlayList: state.player.showPlayList,
      mode: state.player.mode,
      currentIndex: state.player.currentIndex,
      sequencePlayList: state.player.sequencePlayList,
      playList: state.player.playList,
    }),
    shallowEqual
  );
  const dispatch = useDispatch();
  const togglePlayingDispatch = useCallback(
    (data) => {
      dispatch(changePlayingStateAction(data));
    },
    [dispatch]
  );
  const toggleFullScreenDispatch = useCallback(
    (data) => {
      dispatch(changeFullScreenAction(data));
    },
    [dispatch]
  );
  //改变playList
  const togglePlayListDispatch = useCallback(
    (data) => {
      dispatch(changeShowPlayListAction(data));
    },
    [dispatch]
  );
  const changeCurrentIndexDispatch = useCallback(
    (index) => {
      dispatch(changeCurrentIndexAction(index));
    },
    [dispatch]
  );
  const changeCurrentDispatch = useCallback(
    (data) => {
      dispatch(changeCurrentSongAction(data));
    },
    [dispatch]
  );
  //改变mode
  const changeModeDispatch = useCallback(
    (data) => {
      dispatch(changePlayModeAction(data));
    },
    [dispatch]
  );
  const changePlayListDispatch = useCallback(
    (data) => {
      dispatch(changePlayListAction(data));
    },
    [dispatch]
  );
  const clickPlaying = (e, state) => {
    e.stopPropagation();
    togglePlayingDispatch(state);
  };

  const updateTime = (e) => {
    setCurrentTime(e.target.currentTime);
  };
  const onProgressChange = (curPercent) => {
    const newTime = curPercent * duration;
    setCurrentTime(newTime);
    audioRef.current.currentTime = newTime;
    if (!playing) {
      togglePlayingDispatch(true);
    }
  };
  // 一首歌循环
  const handleLoop = () => {
    audioRef.current.currentTime = 0;
    changePlayingStateAction(true);
    audioRef.current.play();
  };
  // 播放前一首
  const handlePrev = useCallback(() => {
    // 播放列表只有一首歌时单曲循环
    if (playList.length === 1) {
      handleLoop();
      // 如果此时没有播放 开启播放
      if (!playing) togglePlayingDispatch(true);
      return;
    }
    // 如果当前是第一首
    let index = currentIndex - 1;
    // 那么前一首就是列表里面的最后一首
    if (index < 0) index = playList.length - 1;
    // 如果此时没有播放 开启播放
    if (!playing) togglePlayingDispatch(true);
    // 改变currentIndex
    changeCurrentIndexDispatch(index);
  }, [
    togglePlayingDispatch,
    changeCurrentIndexDispatch,
    playList.length,
    playing,
    currentIndex,
  ]);
  // 播放下一首
  const handleNext = useCallback(() => {
    // 播放列表只有一首歌时单曲循环
    if (playList.length === 1) {
      handleLoop();
      // 如果此时没有播放 开启播放
      if (!playing) togglePlayingDispatch(true);
      return;
    }
    let index = currentIndex + 1;
    // 如果是最后一首，那么下一首是第一首
    if (index === playList.length) index = 0;
    // 如果此时没有播放 开启播放
    if (!playing) togglePlayingDispatch(true);
    changeCurrentIndexDispatch(index);
  }, [
    togglePlayingDispatch,
    changeCurrentIndexDispatch,
    playList.length,
    playing,
    currentIndex,
  ]);
  const changeMode = useCallback(() => {
    let newMode = (mode + 1) % 3;
    if (newMode === 0) {
      // 顺序模式
      changePlayListDispatch(sequencePlayList);
      const index = findIndex(currentSong, sequencePlayList);
      changeCurrentIndexDispatch(index);
      setModeText('顺序循环');
    } else if (newMode === 1) {
      // 单曲循环
      // changePlayListDispatch(sequencePlayList);
      setModeText('单曲循环');
    } else if (newMode === 2) {
      // 随机播放
      const newList = shuffle(sequencePlayList);
      const index = findIndex(currentSong, newList);
      changePlayListDispatch(newList);
      changeCurrentIndexDispatch(index);
      setModeText('随机播放');
    }
    changeModeDispatch(newMode);
    toastRef.current.show();
  }, [
    changeCurrentIndexDispatch,
    changeModeDispatch,
    changePlayListDispatch,
    currentSong,
    mode,
    sequencePlayList,
  ]);
  const handleEnd = () => {
    if (mode === playMode.loop) {
      handleLoop();
    } else {
      handleNext();
    }
  };
  const handleError = () => {
    songReady.current = true;
    handleNext();
    console.log('切换歌曲太快，播放出错');
  };
  const getLyric = (id) => {
    let lyric = '';
    getLyricRequest(id)
      .then((data) => {
        console.log(data.lrc.lyric);
        lyric = data.lrc.lyric;
        if (!lyric) {
          currentLyric.current = null;
          return;
        }
        currentLyric.current = new Lyric(lyric);
      })
      .catch(() => {
        songReady.current = true;
        audioRef.current.play();
      });
  };
  useEffect(() => {
    if (
      !playList.length ||
      currentIndex === -1 ||
      !playList[currentIndex] ||
      playList[currentIndex].id === preSong.id ||
      !songReady.current // 标志位为 false
    )
      return;
    const current = playList[currentIndex];
    setPreSong(current);
    songReady.current = false; // 把标志位置为 false, 表示现在新的资源没有缓冲完成，不能切歌
    changeCurrentDispatch(current); //赋值currentSong
    audioRef.current.src = getSongUrl(current.id);
    setTimeout(() => {
      audioRef.current.play().then(() => {
        songReady.current = true;
      });
    });
    togglePlayingDispatch(true); //播放状态
    getLyric(current.id);
    setCurrentTime(0); //从头开始播放
    setDuration((current.dt / 1000) | 0); //时长 (current.dt / 1000) | 0 向下取整
  }, [playList, currentIndex, togglePlayingDispatch, changeCurrentDispatch, preSong]);
  useEffect(() => {
    playing ? audioRef.current.play() : audioRef.current.pause();
  }, [playing]);
  return (
    <div>
      {isEmptyObject(currentSong) ? null : (
        <MiniPlayer
          song={currentSong}
          fullScreen={fullScreen}
          playing={playing}
          percent={percent} // 进度
          toggleFullScreen={toggleFullScreenDispatch}
          clickPlaying={clickPlaying}
          togglePlayList={togglePlayListDispatch}
        />
      )}
      {isEmptyObject(currentSong) ? null : (
        <NormalPlayer
          song={currentSong}
          fullScreen={fullScreen}
          playing={playing}
          duration={duration} // 总时长
          currentTime={currentTime} //播放时间
          percent={percent} // 进度
          toggleFullScreen={toggleFullScreenDispatch}
          clickPlaying={clickPlaying}
          onProgressChange={onProgressChange}
          handlePrev={handlePrev}
          handleNext={handleNext}
          mode={mode}
          changeMode={changeMode}
          togglePlayList={togglePlayListDispatch}
        />
      )}
      <audio
        ref={audioRef}
        onTimeUpdate={updateTime}
        onEnded={handleEnd}
        onError={handleError}
      ></audio>
      <PlayList></PlayList>
      <Toast text={modeText} ref={toastRef} />
    </div>
  );
});
